/* ***** BEGIN LICENSE BLOCK *****
* (C)opyright 2008-2009 Aplix Corporation. anselm@aplixcorp.com
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
* ***** END LICENSE BLOCK ***** */

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <tchar.h>

#include "npapi/npapi.h"
#include "npapi/nptypes.h"
#include "npapi/npfunctions.h"
#include "npapi/npruntime.h"

#include "worker.h"

static NPObject        *so       = NULL;
static NPNetscapeFuncs *npnfuncs = NULL;
static NPP              inst     = NULL;
static char  strUrl[1000];
/* NPN */

static Worker *worker = NULL;



#if _MSC_VER >=   1300         //   for   VC   7.0 
//   from   ATL   7.0   sources 
#ifndef  _delayimp_h 
extern   "C"   IMAGE_DOS_HEADER   __ImageBase; 
#endif 
#endif 

HMODULE   GetCurrentModule() 
{ 
#if _MSC_VER   <   1300         //   earlier   than   .NET   compiler   (VC   6.0) 

	//   Here 's   a   trick   that   will   get   you   the   handle   of   the   module 
	//   you 're   running   in   without   any   a-priori   knowledge: 
	//   http://www.dotnet247.com/247reference/msgs/13/65259.aspx 

	MEMORY_BASIC_INFORMATION   mbi; 
	static   int   dummy; 
	VirtualQuery(   &dummy,   &mbi,   sizeof(mbi)   ); 

	return   reinterpret_cast <HMODULE> (mbi.AllocationBase); 

#else         //   VC   7.0 

	//   from   ATL   7.0   sources 

	return   reinterpret_cast <HMODULE> (&__ImageBase); 
#endif 
} 


//UINT WM_ADD_URL = ::RegisterWindowMessage(_T("WM_WVD_ADDURL"));

static HWND		g_Win = 0;

//static 
void logmsg(const char *msg) {
	FILE *out = fopen("\\npsimple.log", "a");
	if(out) {
		fputs(msg, out);
		fclose(out);
	}
}

static bool
hasMethod(NPObject* obj, NPIdentifier methodName) {
	logmsg("npsimple: hasMethod\n");
	return true;
}

static bool
invokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result) {
	logmsg("npsimple: invokeDefault\n");
	result->type = NPVariantType_Int32;
	result->value.intValue = 42;
	return true;
}



static BOOL GetWVDWin(){

	if(g_Win && ::IsWindow(g_Win)) return TRUE;

	logmsg("GetWVDWin,not found ,stry start");
	g_Win = NULL;

	g_Win = ::FindWindow(NULL,_T("Sothink Canvas To Video"));
	return (g_Win != NULL);

	//do not auto start web video downloader
	/*int nTry = 0;
	while(g_Win == NULL){

		g_Win = ::FindWindow(NULL,_T("Sothink Web Video Downloader"));

		if(g_Win) 
			return TRUE;
		else{
			logmsg("start web video downloader \n");
			STARTUPINFO si = {0};
			PROCESS_INFORMATION pi = {0};
			ZeroMemory( &si, sizeof(si) );
			si.cb = sizeof(si);
			si.dwFlags = STARTF_USESHOWWINDOW;
			si.wShowWindow = SW_SHOWMINIMIZED;
			ZeroMemory( &pi, sizeof(pi) );

			TCHAR path[1024]=_T("");

#ifdef _DEBUG
			wcscpy(path,_T("G:\\work\\Video_DB\\04_Code\\01_Products\\WebVideoDownloader\\VideoDownloader\\Debug\\VideoDownloader.exe"));
#else

			HKEY hKey = NULL;

			DWORD err = 0;
			if (::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\SourceTec\\Sothink Web Video Downloader\\"), 0, KEY_READ , &hKey) == ERROR_SUCCESS)
			{

				DWORD dwType = 0;
				DWORD dwSize = 1024;

				err = ::RegQueryValueEx(hKey, _T("Binary Path"), 0, &dwType, (LPBYTE)path, &dwSize);
				if (err == ERROR_SUCCESS)
				{
				}else{
					char errmsg[256];
					sprintf(errmsg,"can not read reg key due to error %d\n",err);
					logmsg(errmsg);
				}
			}else
				logmsg("can not open reg key\n");
			::RegCloseKey(hKey);


#endif


			char str[1024];
			::WideCharToMultiByte(CP_ACP,0,path,wcslen(path),str,1024,NULL,0);
			logmsg("path is:");
			logmsg(str);
			logmsg("\n");

			if(wcscmp(path,_T(""))== 0) return FALSE;

			if( !CreateProcess( NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) )
			{
				_tprintf( _T("CreateProcess failed - %d\n"), GetLastError() );
				logmsg("create process failed");

				return FALSE;
			}

		

			CloseHandle( pi.hProcess );
			CloseHandle( pi.hThread );

			nTry++;
			if(nTry>3) break;
			Sleep(1000);
		}
	}*/
	return FALSE;
}

static bool IsCaptureOn(){

	bool is_on = false;

	HKEY hKey = NULL;
	TCHAR path[1024]=_T("");
	DWORD err = 0;
	if (::RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\SourceTec\\Sothink Web Video Downloader\\Options\\Capture"), 0, KEY_READ , &hKey) == ERROR_SUCCESS)
	{

		DWORD dwType = 0;
		DWORD dwSize = 1024;

		err = ::RegQueryValueEx(hKey, _T("Selected Browsers"), 0, &dwType, (LPBYTE)path, &dwSize);
		if (err == ERROR_SUCCESS)
		{
			if(strstr((const char*)path,"chrome.exe")!= NULL) is_on = true;

		}else{
			char errmsg[256];
			sprintf(errmsg,"can not read reg key due to error %d\n",err);
			logmsg(errmsg);
		}
	}else
		logmsg("can not open reg key\n");
	::RegCloseKey(hKey);
	return is_on;
}

static bool StopCapture(){
if(GetWVDWin()){
		logmsg("StopCapture()\n");

		/*COPYDATASTRUCT cpd;
		//cpd.dwData = strURL.UTF8Length;
		cpd.cbData =4+4*sizeof(UINT);
		cpd.lpData = malloc(cpd.cbData); 
		//header: 8bytes LengthOfUa/LengthOfURL
		strcpy((char*)cpd.lpData,"STOP");
		

	


		::SendMessage(g_Win,WM_COPYDATA,NULL,LPARAM(&cpd));
		free(cpd.lpData);*/

		if(worker)
			worker->stop_capture();
		return true;
	}
	return true;

}


static bool GetURL(NPVariant &url){

	NPVariant v_win;
	NPVariant v_loc;
	NPObject  *winObj;
	NPObject  *locObj;
	//get Dom window

	NPError err = npnfuncs->getvalue(inst,NPNVWindowNPObject,&v_win);
	if(err != NPERR_NO_ERROR){
		logmsg("npnfuncs->getvalue error \n");
		return false;
	}
	logmsg("we get dom window\n");
	winObj = v_win.value.objectValue;

	if(npnfuncs->getstringidentifier == NULL){
		logmsg("no get identifier function\n");
		return false;
	}
	//create a location identifier
	NPIdentifier  identifier = npnfuncs->getstringidentifier("location");
	if(identifier == NULL){
		logmsg("get location identifier error\n");
		return false;
	}
	//get location property
	
	bool b1 =	npnfuncs->getproperty(inst,winObj,identifier,&v_loc);
	if(!b1){
		logmsg("npnfuncs->getproperty erro \n");
		return false;
	}
	logmsg("we get location object\n");
	locObj = v_loc.value.objectValue;
	
	identifier = npnfuncs->getstringidentifier("href");
	if(identifier == NULL){
		logmsg("get href identifier error");
		return false;
	}
	bool b2  = npnfuncs->getproperty(inst,locObj,identifier,&url);
	
	//NPN_ReleaseVariantValue(&v_loc);
	//NPN_ReleaseVariantValue(&v_win);

	return b2;
}


static void Connect(){
	//connect to named pipe,
	//create a new thread to establish a connection to a named pipe.
	//issue command to record 
	//listen to the state information
	//and issue command to stop 
}



void ShowProgressCallback(LPVOID param)
{
	int prog = (int)param;
	//get window
	NPObject* window = NULL;
	NPError err = npnfuncs->getvalue(inst, NPNVWindowNPObject, &window);

	if(err != NPERR_NO_ERROR){
		logmsg("npnfuncs->getvalue error \n");
		return ;
	}
	//window = v_win.value.objectValue;
	//get showprog
	//NPIdentifier id = npnfuncs->getstringidentifier("showprog");
	NPIdentifier id = npnfuncs->getstringidentifier("showprog");

	if(id == NULL){
		logmsg("get showprog identifier error\n");
		return ;
	}	
	//NPVARIANT_TO_OBJECT

	NPVariant v;

	INT32_TO_NPVARIANT(prog, v);
	NPVariant args[] = { v };
	NPVariant result;

	INT32_TO_NPVARIANT(0, result);


	bool res = npnfuncs->invoke(inst, window, id, args,
		sizeof(args) / sizeof(args[0]),
		&result);

	npnfuncs->releasevariantvalue(&result);
	npnfuncs->releaseobject(window);
}

void schedule_showprog(int prog){
	
	npnfuncs->pluginthreadasynccall(inst,ShowProgressCallback,(void*)prog);
}

static bool StartCapture(NPString *url,UINT left,UINT top,UINT width,UINT height){
	if(GetWVDWin()){
		logmsg("find web video downloader\n");

		
		if(worker)
		{
			char buf[100];
			sprintf(buf,"CRECO %d %d %d %d",left,top,width,height);
			worker->start_capture(left,top,width,height);
		}

		/*COPYDATASTRUCT cpd;
		cpd.cbData =4+4*sizeof(UINT)+url->UTF8Length;
		cpd.lpData = malloc(cpd.cbData); 
		strcpy((char*)cpd.lpData,"STAT");
		UINT *ptr = (UINT*)cpd.lpData+1;
		*ptr++ = left;
		*ptr++ = top;
		*ptr++ = width;
		*ptr++ = height;


		
		char *ptr1 = (char*)cpd.lpData+4+4*sizeof(UINT);
		memcpy(ptr1,url->UTF8Characters,url->UTF8Length);
		

		int *iPtr =(int*) cpd.lpData;
		*iPtr = strUA.UTF8Length;
		*(iPtr+1) = strURL.UTF8Length;
		
		char *ptr = (char*)cpd.lpData+8;
		memcpy(ptr,strUA.UTF8Characters,strUA.UTF8Length);ptr+=strUA.UTF8Length;*ptr='\0';ptr++;
		memcpy(ptr,strURL.UTF8Characters,strURL.UTF8Length);ptr+=strURL.UTF8Length;*ptr='\0';ptr++;
		memcpy(ptr,mime.UTF8Characters,mime.UTF8Length);ptr+=mime.UTF8Length;*ptr='\0';


		::SendMessage(g_Win,WM_COPYDATA,NULL,LPARAM(&cpd));
		free(cpd.lpData);*/


		
		return true;
	}else{
	
		MessageBoxA(NULL,"connection lost, please make sure cnvas2video is running and retry","Error",MB_OK);
	
	}
		
	
	return true;
}

static bool
invoke(NPObject* obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result) {
	logmsg("npsimple: invoke\n");
	int error = 1;
	char *name = npnfuncs->utf8fromidentifier(methodName);
	if(name) {
		if(!strcmp(name,"start_capture")){
			logmsg("npsimple: invoke start_capture\n");
			char tmp[100];
			sprintf(tmp,"argcount = %d,arg type :%d\n",argCount,args[0].type);
			logmsg(tmp);
			if(argCount == 5 && args[0].type == NPVariantType_String && args[1].type == NPVariantType_Double && args[2].type == NPVariantType_Double &&args[3].type == NPVariantType_Double) {
				logmsg("npsimple: start_capture\n");

				NPString url = NPVARIANT_TO_STRING(args[0]);

				UINT left = NPVARIANT_TO_DOUBLE(args[1]);
				UINT top = NPVARIANT_TO_DOUBLE(args[2]);
				UINT width = NPVARIANT_TO_DOUBLE(args[3]);
				UINT32 height = NPVARIANT_TO_DOUBLE(args[4]);
				

				return StartCapture(&url,left,top,width,height);
			}
			return false;
		}else if(!strcmp(name,"stop_capture")){
			logmsg("npsimple: invoke stop_capture\n");
			if(argCount == 0){
		      result->type = NPVariantType_Bool;
			  result->value.boolValue = StopCapture();
			  return true;
			}
		}else if(!strcmp(name,"is_recording")){
			logmsg("npsimple: invoke is_recording\n");
			if(argCount == 0){
				result->type = NPVariantType_Bool;
				result->value.boolValue = worker->is_recording();
				return true;
			}			
		}else if(!strcmp(name, "foo")) {
			logmsg("npsimple: invoke foo\n");
			return invokeDefault(obj, args, argCount, result);
		}
		else if(!strcmp(name, "callback")) {
			if(argCount == 1 && args[0].type == NPVariantType_Object) {
				static NPVariant v, r;
				const char kHello[] = "Hello";
				char *txt = (char *)npnfuncs->memalloc(strlen(kHello));

				logmsg("npsimple: invoke callback function\n");
				memcpy(txt, kHello, strlen(kHello));
				STRINGN_TO_NPVARIANT(txt, strlen(kHello), v);
				/* INT32_TO_NPVARIANT(42, v); */
				if(npnfuncs->invokeDefault(inst, NPVARIANT_TO_OBJECT(args[0]), &v, 1, &r))
					return invokeDefault(obj, args, argCount, result);
			}
		}
	}
	// aim exception handling
	npnfuncs->setexception(obj, "exception during invocation");
	return false;
}



static bool
hasProperty(NPObject *obj, NPIdentifier propertyName) {
	logmsg("npsimple: hasProperty\n");
	return false;
}

static bool
getProperty(NPObject *obj, NPIdentifier propertyName, NPVariant *result) {
	logmsg("npsimple: getProperty\n");
	return false;
}

static NPClass npcRefObject = {
	NP_CLASS_STRUCT_VERSION,
	NULL,
	NULL,
	NULL,
	hasMethod,
	invoke,
	invokeDefault,
	hasProperty,
	getProperty,
	NULL,
	NULL,
};

/* NPP */

static NPError
nevv(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved) {
	inst = instance;
	logmsg("npsimple: new\n");
	return NPERR_NO_ERROR;
}

static NPError
destroy(NPP instance, NPSavedData **save) {
	if(worker){
				//worker->exit();
				delete worker;
				worker = NULL;

    }
	if(so)
		npnfuncs->releaseobject(so);
	so = NULL;
	
	logmsg("npsimple: destroy\n");
	return NPERR_NO_ERROR;
}

static NPError
getValue(NPP instance, NPPVariable variable, void *value) {
	inst = instance;
	switch(variable) {
	default:
		logmsg("npsimple: getvalue - default\n");
		return NPERR_GENERIC_ERROR;
	case NPPVpluginNameString:
		logmsg("npsimple: getvalue - name string\n");
		*((char **)value) = "AplixFooPlugin";
		break;
	case NPPVpluginDescriptionString:
		logmsg("npsimple: getvalue - description string\n");
		*((char **)value) = "<a href=\"http://www.aplix.co.jp/\">AplixFooPlugin</a> plugin.";
		break;
	case NPPVpluginScriptableNPObject:
		logmsg("npsimple: getvalue - scriptable object\n");
		if(!so)
			so = npnfuncs->createobject(instance, &npcRefObject);
		npnfuncs->retainobject(so);
		*(NPObject **)value = so;
		break;
	case NPPVpluginNeedsXEmbed:
		logmsg("npsimple: getvalue - xembed\n");
		*((char *)value) = 0;
		break;
	}
	return NPERR_NO_ERROR;
}

static NPError /* expected by Safari on Darwin */
handleEvent(NPP instance, void *ev) {
	inst = instance;
	logmsg("npsimple: handleEvent\n");
	return NPERR_NO_ERROR;
}


static  NPError  newStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype){

	inst = instance;
	strcpy(strUrl,stream->url);
	logmsg("npsimple: newStream\n");
	return NPERR_NO_ERROR;
}

static NPError /* expected by Opera */
setWindow(NPP instance, NPWindow* pNPWindow) {
	inst = instance;
	logmsg("npsimple: setWindow\n");
	return NPERR_NO_ERROR;
}




/* EXPORT */
#ifdef __cplusplus
extern "C" {
#endif

	NPError OSCALL
		NP_GetEntryPoints(NPPluginFuncs *nppfuncs) {
			logmsg("npsimple: NP_GetEntryPoints\n");
			nppfuncs->version       = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
			nppfuncs->newp          = nevv;
			nppfuncs->destroy       = destroy;
			nppfuncs->getvalue      = getValue;
			nppfuncs->event         = handleEvent;
			nppfuncs->setwindow     = setWindow;
			nppfuncs->newstream		= newStream;

			return NPERR_NO_ERROR;
	}

#ifndef HIBYTE
#define HIBYTE(x) ((((uint32)(x)) & 0xff00) >> 8)
#endif




	NPError OSCALL
		NP_Initialize(NPNetscapeFuncs *npnf)
	{

		logmsg("npsimple: NP_Initialize\n");
		if(npnf == NULL)
			return NPERR_INVALID_FUNCTABLE_ERROR;

		if(HIBYTE(npnf->version) > NP_VERSION_MAJOR)
			return NPERR_INCOMPATIBLE_VERSION_ERROR;

		npnfuncs = npnf;

		if(worker == NULL){
			logmsg("npsimple: create worker\n");
			worker = new Worker();
			if(!worker->init())
				logmsg("npsimple:  worker init failed\n");
		}



		return NPERR_NO_ERROR;
	}

	NPError
		OSCALL NP_Shutdown() {
			logmsg("npsimple: delete worker\n");
			if(worker){
				worker->close();
				delete worker;
				worker = NULL;
			}
			logmsg("npsimple: NP_Shutdown\n");
			return NPERR_NO_ERROR;
	}

	const char *
		NP_GetMIMEDescription(void) {
			logmsg("npsimple: NP_GetMIMEDescription\n");
			return "application/x-sothink-c2v:.c2v:support@sothink.com";
	}

	NPError OSCALL /* needs to be present for WebKit based browsers */
		NP_GetValue(void *npp, NPPVariable variable, void *value) {
			inst = (NPP)npp;
			return getValue((NPP)npp, variable, value);
	}



#ifdef __cplusplus
}
#endif
